package org.chen.scene.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.chen.common.scene.dto.*;
import org.chen.common.scene.dto.api.CtripApiResponse;
import org.chen.common.scene.entity.Attraction;
import org.chen.common.scene.entity.AttractionTag;
import org.chen.common.scene.entity.FilterOption;
import org.chen.common.scene.ollama.TourRecommendationResponse;
import org.chen.common.scene.task.AttractionResult;
import org.chen.scene.mapper.AttractionMapper;
import org.chen.scene.mapper.AttractionTagMapper;
import org.chen.scene.mapper.FilterOptionMapper;
import org.chen.scene.qdrant.QdrantService;
import org.chen.scene.service.geocode.GeocodingService;
import org.chen.scene.service.ollama.TourRecommendationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors;

@Slf4j
@Service
public class AttractionService extends ServiceImpl<AttractionMapper, Attraction> implements IService<Attraction> , IAttractionService {
    
    @Autowired
    private CtripApiService ctripApiService;
    
    @Autowired
    private AttractionTagMapper attractionTagMapper;
    
    @Autowired
    private FilterOptionMapper filterOptionMapper;

    @Autowired
    private WebScrapingService webScrapingService;

    @Autowired
    @Qualifier("attractionTaskExecutor")
    private TaskExecutor attractionTaskExecutor;

    @Autowired
    private GeocodingService geocodingService;

    @Autowired
    private QdrantService qdrantService;

    @Autowired
    private TourRecommendationService tourRecommendationService;

    /**
     * 保存景点标签
     */
    private void saveAttractionTags(Long poiId, List<String> tags, String tagType) {
        // 删除已有标签
        QueryWrapper<AttractionTag> wrapper = new QueryWrapper<>();
        wrapper.eq("poi_id", poiId).eq("tag_type", tagType);
        attractionTagMapper.delete(wrapper);
        
        // 添加新标签
        for (String tag : tags) {
            AttractionTag attractionTag = new AttractionTag();
            attractionTag.setPoiId(poiId);
            attractionTag.setTagName(tag);
            attractionTag.setTagType(tagType);
            attractionTagMapper.insert(attractionTag);
        }
    }
    
    /**
     * 保存筛选条件
     */
    private void saveFilterOptions(CtripApiResponse.Filters filters) {
        if (!CollectionUtils.isEmpty(filters.getAttractionTypeFilter())) {
            for (CtripApiResponse.Filters.FilterGroup group : filters.getAttractionTypeFilter()) {
                saveFilterGroup(group);
            }
        }
    }
    
    private void saveFilterGroup(CtripApiResponse.Filters.FilterGroup group) {
        if (!CollectionUtils.isEmpty(group.getFilterDteails())) {
            for (CtripApiResponse.Filters.FilterGroup.FilterDetail detail : group.getFilterDteails()) {
                // 检查是否已存在
                QueryWrapper<FilterOption> wrapper = new QueryWrapper<>();
                wrapper.eq("filter_text", detail.getText())
                       .eq("filter_id", detail.getId());
                
                if (filterOptionMapper.selectCount(wrapper) == 0) {
                    FilterOption option = new FilterOption();
                    option.setFilterName(group.getName());
                    option.setFilterText(detail.getText());
                    option.setFilterId(detail.getId());
                    filterOptionMapper.insert(option);
                }
            }
        }
    }

    public PagedAttractionResponse getAttractions(AttractionQueryDTO queryDTO) {
        AttractionResult result = getAttractionsWithResult(queryDTO);
        return result.getPagedResponse();
    }

    /**
     * 并发处理景点数据：保存基本信息 + 并发抓取详情 + 统计数据
     */
    private ProcessResult processConcurrentAttractionsWithStats(List<CtripApiResponse.AttractionItem> apiItems) {
        ProcessResult processResult = new ProcessResult();
        processResult.setAttractionList(new ArrayList<>());

        List<CompletableFuture<AttractionProcessResult>> futures = new ArrayList<>();

        log.info("开始并发处理 {} 个景点", apiItems.size());
        long startTime = System.currentTimeMillis();

        for (CtripApiResponse.AttractionItem item : apiItems) {
            if (item.getCard() != null) {
                CtripApiResponse.AttractionItem.Card card = item.getCard();

                // 提交并发任务：保存基本信息 + 抓取详情 + 统计
                CompletableFuture<AttractionProcessResult> future = CompletableFuture.supplyAsync(() -> {
                    return processAttractionWithDetailsAndStats(card);
                }, attractionTaskExecutor);

                futures.add(future);
            }
        }

        // 等待所有任务完成，设置超时时间防止无限等待
        try {
            CompletableFuture<Void> allTasks = CompletableFuture.allOf(
                    futures.toArray(new CompletableFuture[0])
            );

            // 设置30秒超时
            allTasks.get(30, TimeUnit.SECONDS);

            // 收集结果并统计
            int newCount = 0;
            int updateCount = 0;

            for (CompletableFuture<AttractionProcessResult> future : futures) {
                try {
                    AttractionProcessResult result = future.get();
                    if (result != null && result.getDto() != null) {
                        processResult.getAttractionList().add(result.getDto());

                        if (result.isNewRecord()) {
                            newCount++;
                        } else if (result.isUpdated()) {
                            updateCount++;
                        }
                    }
                } catch (Exception e) {
                    log.error("获取景点处理结果失败", e);
                }
            }

            processResult.setNewCount(newCount);
            processResult.setUpdateCount(updateCount);

        } catch (TimeoutException e) {
            log.warn("并发处理景点超时，收集已完成的结果");
            // 收集已完成的结果
            int newCount = 0;
            int updateCount = 0;

            for (CompletableFuture<AttractionProcessResult> future : futures) {
                if (future.isDone() && !future.isCompletedExceptionally()) {
                    try {
                        AttractionProcessResult result = future.get();
                        if (result != null && result.getDto() != null) {
                            processResult.getAttractionList().add(result.getDto());

                            if (result.isNewRecord()) {
                                newCount++;
                            } else if (result.isUpdated()) {
                                updateCount++;
                            }
                        }
                    } catch (Exception ex) {
                        log.error("获取已完成的景点结果失败", ex);
                    }
                }
            }

            processResult.setNewCount(newCount);
            processResult.setUpdateCount(updateCount);
        } catch (Exception e) {
            log.error("并发处理景点异常", e);
        }

        long endTime = System.currentTimeMillis();
        log.info("并发处理完成: {} 个景点，新增: {}, 更新: {}, 耗时 {} ms",
                processResult.getAttractionList().size(), processResult.getNewCount(),
                processResult.getUpdateCount(), (endTime - startTime));

        return processResult;
    }

    // 4. 创建单个景点处理结果类
    @Data
    public static class AttractionProcessResult {
        private AttractionResponseDTO dto;
        private boolean isNewRecord = false;
        private boolean isUpdated = false;
    }

    /**
     * 处理单个景点：保存基本信息 + 抓取详情 + 统计
     */
    private AttractionProcessResult processAttractionWithDetailsAndStats(CtripApiResponse.AttractionItem.Card card) {
        String threadName = Thread.currentThread().getName();
        AttractionProcessResult result = new AttractionProcessResult();

        try {
            log.debug("开始处理景点 {} [线程: {}]", card.getPoiId(), threadName);

            // 1. 检查是否已存在
            QueryWrapper<Attraction> wrapper = new QueryWrapper<>();
            wrapper.eq("poi_id", card.getPoiId());
            Attraction existingAttraction = this.getOne(wrapper);

            boolean isNewRecord = (existingAttraction == null);
            result.setNewRecord(isNewRecord);

            // 2. 保存基本景点数据并获取保存结果
            AttractionSaveResult saveResult = saveAttractionDataWithStats(card, existingAttraction);
            result.setUpdated(saveResult.isUpdated());

            Attraction attraction = saveResult.getAttraction();

            // 3. 构建基本DTO
            AttractionResponseDTO dto = buildBasicAttractionDTO(card);
            dto.setVisitHours(attraction.getVisitHours());

            // 4. 尝试抓取详情
            if (card.getDetailUrl() != null && !card.getDetailUrl().trim().isEmpty()) {
                try {
                    boolean needScrape = true;
                    if (attraction.getDetailFetched() && hasCompleteDetails(attraction)) {
                        // 如果已有完整详情，直接使用
                        dto.setIntroduction(attraction.getIntroduction());
                        dto.setOpeningHours(attraction.getOpeningHours());
                        dto.setImages(attraction.getImages());
                        needScrape = false;
                        log.debug("景点 {} 已有详情，跳过抓取 [线程: {}]", card.getPoiId(), threadName);
                    }

                    if (needScrape) {
                        // 抓取详情（设置较短超时时间）
                        Map<String, String> details = scrapeWithTimeout(card.getDetailUrl());

                        if (details != null && !details.isEmpty()) {
                            // 更新数据库
                            updateAttractionDetails(card.getPoiId(), details);
                            result.setUpdated(true); // 标记为已更新

                            // 更新DTO
                            dto.setIntroduction(details.getOrDefault("introduction", ""));
                            dto.setOpeningHours(details.getOrDefault("openingHours", ""));
                            dto.setImages(details.getOrDefault("images", ""));

                            log.debug("成功抓取景点 {} 详情 [线程: {}]", card.getPoiId(), threadName);
                        }
                    }
                } catch (Exception e) {
                    log.warn("抓取景点 {} 详情失败，使用基本信息 [线程: {}]: {}",
                            card.getPoiId(), threadName, e.getMessage());
                }
            }

            result.setDto(dto);

            log.debug("完成处理景点 {} [线程: {}], 新增: {}, 更新: {}",
                    card.getPoiId(), threadName, result.isNewRecord(), result.isUpdated());

            // 5. 保存到向量数据库
            if (attraction.getVisitHours() != null && !attraction.getVisitHours().trim().isEmpty()) {
                qdrantService.saveAttractionVector(attraction);
            }

            return result;

        } catch (Exception e) {
            log.error("处理景点 {} 失败 [线程: {}]", card.getPoiId(), threadName, e);
            // 即使失败也返回基本信息
            result.setDto(buildBasicAttractionDTO(card));
            return result;
        }
    }

    // 5. 创建保存结果类
    @Data
    public static class AttractionSaveResult {
        private Attraction attraction;
        private boolean isUpdated = false;
    }

    /**
     * 检查景点是否已有完整详情
     */
    private boolean hasCompleteDetails(Attraction attraction) {
        return (attraction.getIntroduction() != null && !attraction.getIntroduction().isEmpty()) ||
                (attraction.getOpeningHours() != null && !attraction.getOpeningHours().isEmpty()) ||
                (attraction.getImages() != null && !attraction.getImages().isEmpty());
    }

    /**
     * 抓取方法
     */
    public Map<String, String> scrapeWithTimeout(String detailUrl) {
        try {
            return webScrapingService.scrapeAttractionDetails(detailUrl);
        } catch (Exception e) {
            log.error("抓取详情失败: {}", detailUrl, e);
            return Collections.emptyMap();
        }
    }

    /**
     * 构建基本景点DTO
     */
    private AttractionResponseDTO buildBasicAttractionDTO(CtripApiResponse.AttractionItem.Card card) {
        AttractionResponseDTO dto = new AttractionResponseDTO();

        dto.setPoiId(card.getPoiId());
        dto.setPoiName(card.getPoiName());
        dto.setDistrictName(card.getDistrictName());
        dto.setCommentCount(card.getCommentCount());
        dto.setCommentScore(card.getCommentScore());
        dto.setSightLevel(card.getSightLevelStr());
        dto.setCoverImageUrl(card.getCoverImageUrl());
        dto.setDistanceStr(card.getDistanceStr());
        dto.setMarketPrice(card.getMarketPrice());
        dto.setPrice(card.getPrice());
        dto.setPriceTypeDesc(card.getPriceTypeDesc());
        dto.setIsFree(card.getIsFree());
        dto.setHeatScore(card.getHeatScore());
        dto.setDetailUrl(card.getDetailUrl());
        dto.setSightCategoryInfo(card.getSightCategoryInfo());

        // 处理坐标信息
        if (card.getCoordinate() != null) {
            dto.setLatitude(card.getCoordinate().getLatitude());
            dto.setLongitude(card.getCoordinate().getLongitude());
        }

        // 处理特色描述
        if (!CollectionUtils.isEmpty(card.getShortFeatures())) {
            dto.setShortFeatures(String.join(",", card.getShortFeatures()));
        }

        // 处理标签
        List<String> allTags = new ArrayList<>();
        if (!CollectionUtils.isEmpty(card.getTagNameList())) {
            allTags.addAll(card.getTagNameList());
        }
        if (!CollectionUtils.isEmpty(card.getOtherTagList())) {
            List<String> otherTags = card.getOtherTagList().stream()
                    .map(CtripApiResponse.AttractionItem.Card.OtherTag::getName)
                    .toList();
            allTags.addAll(otherTags);
        }
        dto.setTagNames(allTags);

        return dto;
    }

    /**
     * 保存景点基本数据并返回统计信息
     */
    private AttractionSaveResult saveAttractionDataWithStats(CtripApiResponse.AttractionItem.Card card, Attraction existingAttraction) {
        try {
            AttractionSaveResult result = new AttractionSaveResult();
            boolean isUpdated = false;

            Attraction attraction = existingAttraction != null ? existingAttraction : new Attraction();

            // 检查字段是否有变化（对于已存在的记录）
            if (existingAttraction != null) {
                if (!Objects.equals(attraction.getPoiName(), card.getPoiName()) ||
                        !Objects.equals(attraction.getCommentCount(), card.getCommentCount()) ||
                        !Objects.equals(attraction.getCommentScore(), card.getCommentScore()) ||
                        !Objects.equals(attraction.getPrice(), card.getPrice()) ||
                        !Objects.equals(attraction.getHeatScore(), card.getHeatScore())) {
                    isUpdated = true;
                }
            }

            // 设置景点基本信息（保持原逻辑）
            attraction.setPoiId(card.getPoiId());
            attraction.setPoiName(card.getPoiName());
            attraction.setDistrictId(card.getDistrictId());
            attraction.setDistrictName(card.getDistrictName());
            attraction.setCommentCount(card.getCommentCount());
            attraction.setCommentScore(card.getCommentScore());
            attraction.setSightLevel(card.getSightLevelStr());
            attraction.setCoverImageUrl(card.getCoverImageUrl());
            attraction.setMarketPrice(card.getMarketPrice());
            attraction.setPrice(card.getPrice());
            attraction.setPriceType(card.getPriceType());
            attraction.setPriceTypeDesc(card.getPriceTypeDesc());
            attraction.setIsFree(card.getIsFree());
            attraction.setDetailUrl(card.getDetailUrl());
            attraction.setHeatScore(card.getHeatScore());
            attraction.setSightCategoryInfo(card.getSightCategoryInfo());

            if (existingAttraction == null) {
                attraction.setDetailFetched(false);
            }

            // 设置坐标信息
            if (card.getCoordinate() != null) {
                attraction.setLatitude(card.getCoordinate().getLatitude());
                attraction.setLongitude(card.getCoordinate().getLongitude());
                attraction.setCoordinateType(card.getCoordinate().getCoordinateType());
            }

            // 处理特色描述
            if (!CollectionUtils.isEmpty(card.getShortFeatures())) {
                attraction.setShortFeatures(String.join(",", card.getShortFeatures()));
            }

            // 处理游览时间推荐
            if (attraction.getVisitHours() == null){
                TourRecommendationResponse recommendation = tourRecommendationService.getRecommendation(attraction.getPoiName());
                if (recommendation != null) {
                    attraction.setVisitHours(recommendation.getRecommend_Hours());
                }
            }

            // 保存或更新景点
            this.saveOrUpdate(attraction);

            // 保存标签数据
            if (!CollectionUtils.isEmpty(card.getTagNameList())) {
                saveAttractionTags(card.getPoiId(), card.getTagNameList(), "normal");
            }

            // 保存其他标签
            if (!CollectionUtils.isEmpty(card.getOtherTagList())) {
                List<String> otherTags = card.getOtherTagList().stream()
                        .map(CtripApiResponse.AttractionItem.Card.OtherTag::getName)
                        .collect(Collectors.toList());
                saveAttractionTags(card.getPoiId(), otherTags, "other");
            }

            result.setAttraction(attraction);
            result.setUpdated(isUpdated);
            return result;

        } catch (Exception e) {
            log.error("保存景点 {} 基本数据失败", card.getPoiId(), e);
            throw new RuntimeException("保存景点 "+card.getPoiId()+" 基本数据失败: "+ e.getMessage());
        }
    }

    /**
     * 更新景点详细信息
     */
    public void updateAttractionDetails(Long poiId, Map<String, String> details) {
        try {
            QueryWrapper<Attraction> wrapper = new QueryWrapper<>();
            wrapper.eq("poi_id", poiId);
            Attraction attraction = this.getOne(wrapper);

            if (attraction != null) {
                boolean updated = false;

                // 更新介绍信息
                if (!details.get("introduction").isEmpty() &&
                        (attraction.getIntroduction() == null || attraction.getIntroduction().isEmpty())) {
                    attraction.setIntroduction(details.get("introduction"));
                    updated = true;
                }

                // 更新开放时间
                if (!details.get("openingHours").isEmpty() &&
                        (attraction.getOpeningHours() == null || attraction.getOpeningHours().isEmpty())) {
                    attraction.setOpeningHours(details.get("openingHours"));
                    updated = true;
                }

                // 更新图片信息
                if (!details.get("images").isEmpty() &&
                        (attraction.getImages() == null || attraction.getImages().isEmpty())) {
                    attraction.setImages(details.get("images"));
                    updated = true;
                }


                if (updated) {
                    attraction.setDetailFetched(true);
                    this.updateById(attraction);
                    log.info("成功更新景点 {} 的详细信息", poiId);
                }
            }

        } catch (Exception e) {
            log.error("更新景点 {} 详细信息失败", poiId, e);
        }
    }

    /**
     * 从数据库补充景点详细信息
     */
    private void enrichAttractionDetails(AttractionResponseDTO dto) {
        try {
            QueryWrapper<Attraction> wrapper = new QueryWrapper<>();
            wrapper.eq("poi_id", dto.getPoiId());
            Attraction attraction = this.getOne(wrapper);

            if (attraction != null) {
                if (attraction.getIntroduction() != null && !attraction.getIntroduction().isEmpty()) {
                    dto.setIntroduction(attraction.getIntroduction());
                }
                if (attraction.getOpeningHours() != null && !attraction.getOpeningHours().isEmpty()) {
                    dto.setOpeningHours(attraction.getOpeningHours());
                }
                if (attraction.getImages() != null && !attraction.getImages().isEmpty()) {
                    dto.setImages(attraction.getImages());
                }
                dto.setDistanceStr("");
            }
        } catch (Exception e) {
            log.error("补充景点 {} 详细信息失败", dto.getPoiId(), e);
        }
    }

    /**
     * 从数据库获取景点数据
     */
    public PagedAttractionResponse getAttractionsFromDatabase(AttractionQueryDTO queryDTO) {
        try {
            int offset = (queryDTO.getIndex() - 1) * queryDTO.getCount();

            List<AttractionResponseDTO> results = baseMapper.selectWithTags(
                    queryDTO.getCityName(),
                    queryDTO.getName(),
                    queryDTO.getSortType(),
                    offset,
                    queryDTO.getCount()
            );

            QueryWrapper<Attraction> countWrapper = new QueryWrapper<>();

            // 动态拼接条件
            String cityName = queryDTO.getCityName();
            String name = queryDTO.getName();

            if (cityName != null && !cityName.isEmpty()) {
                countWrapper.eq("district_name", cityName);  // a.district_name = #{cityName}
            }

            if (name != null && !name.isEmpty()) {
                countWrapper.like("poi_name", name);         // a.poi_name LIKE '%#{name}%'
            }

            // 执行 count
            long totalCount = this.count(countWrapper);

            // 构建分页响应
            PagedAttractionResponse response = new PagedAttractionResponse(
                    results,
                    queryDTO.getIndex(),
                    queryDTO.getCount(),
                    (int) totalCount,
                    (offset + queryDTO.getCount()) < totalCount,
                    totalCount > 1000 ? "1万+" : String.valueOf(totalCount)
            );

            log.info("从数据库获取到 {} 个景点数据", results.size());
            return response;

        } catch (Exception e) {
            log.error("从数据库查询景点列表失败", e);
            return new PagedAttractionResponse(new ArrayList<>(), queryDTO.getIndex(), queryDTO.getCount(), 0, false, "0");
        }
    }
    
    /**
     * 获取筛选条件
     */
    public List<FilterOptionDTO> getFilterOptions() {
        try {
            List<FilterOption> allOptions = filterOptionMapper.selectList(null);
            
            // 按筛选类别分组
            Map<String, List<FilterOption>> groupedOptions = allOptions.stream()
                .collect(Collectors.groupingBy(FilterOption::getFilterName));
            
            List<FilterOptionDTO> result = new ArrayList<>();
            
            for (Map.Entry<String, List<FilterOption>> entry : groupedOptions.entrySet()) {
                FilterOptionDTO dto = new FilterOptionDTO();
                dto.setName(entry.getKey());
                
                List<FilterOptionDTO.FilterDetailDTO> details = entry.getValue().stream()
                    .map(option -> {
                        FilterOptionDTO.FilterDetailDTO detail = new FilterOptionDTO.FilterDetailDTO();
                        detail.setText(option.getFilterText());
                        detail.setId(option.getFilterId());
                        return detail;
                    })
                    .collect(Collectors.toList());
                
                dto.setFilterDetails(details);
                result.add(dto);
            }
            
            return result;
            
        } catch (Exception e) {
            log.error("获取筛选条件失败", e);
            return new ArrayList<>();
        }
    }

    /**
     * 查询附近景点
     */
    public PagedAttractionResponse getNearbyAttractions(NearbyAttractionQueryDTO queryDTO) {
        try {
            log.info("开始查询附近景点，参数: {}", queryDTO);

            // 1. 获取查询中心点的经纬度
            BigDecimal centerLat;
            BigDecimal centerLng;

            if (queryDTO.getLatitude() != null && queryDTO.getLongitude() != null) {
                // 直接使用传入的经纬度
                centerLat = queryDTO.getLatitude();
                centerLng = queryDTO.getLongitude();
                log.info("使用传入的经纬度: [{}, {}]", centerLat, centerLng);
            } else if (StringUtils.hasText(queryDTO.getCityName())) {
                // 根据城市名称获取经纬度
                GeocodingService.CityCoordinateResult  coordinate = geocodingService.getCityCoordinates(queryDTO.getCityName());
                if (coordinate == null) {
                    return new PagedAttractionResponse(
                            new ArrayList<>(),
                            queryDTO.getIndex(),
                            queryDTO.getCount(),
                            0,
                            false,
                            "未找到城市: " + queryDTO.getCityName()
                    );
                }
                centerLat = coordinate.getLatitude();
                centerLng = coordinate.getLongitude();
                log.info("城市 {} 的坐标: [{}, {}]", queryDTO.getCityName(), centerLat, centerLng);
            } else {
                throw new IllegalArgumentException("必须提供城市名称或经纬度");
            }

            // 2. 验证坐标
            if (!geocodingService.isValidCoordinates(centerLat, centerLng)) {
                throw new IllegalArgumentException("无效的经纬度坐标");
            }

            // 3. 计算分页参数
            int offset = (queryDTO.getIndex() - 1) * queryDTO.getCount();

            // 4. 查询附近景点
            List<AttractionResponseDTO> attractions = baseMapper.selectNearbyAttractions(
                    centerLat,
                    centerLng,
                    queryDTO.getRadius(),
                    queryDTO.getSortType(),
                    offset,
                    queryDTO.getCount(),
                    queryDTO.getMinScore(),
                    queryDTO.getFreeOnly(),
                    queryDTO.getSightLevel()
            );

            // 5. 处理标签数据
            for (AttractionResponseDTO dto : attractions) {
                if (dto.getTagNames() != null) {
                    String tagNamesStr = dto.getTagNames().toString();
                    if (StringUtils.hasText(tagNamesStr)) {
                        String[] tags = tagNamesStr.split(",");
                        dto.setTagNames(Arrays.asList(tags));
                    }
                }

                // 补充详细信息
                enrichAttractionDetails(dto);
            }

            // 6. 统计总数
            Long totalCount = baseMapper.countNearbyAttractions(
                    centerLat,
                    centerLng,
                    queryDTO.getRadius(),
                    queryDTO.getMinScore(),
                    queryDTO.getFreeOnly(),
                    queryDTO.getSightLevel()
            );

            CityCoordinateResponseDTO cityCoordinateResponseDTO = new CityCoordinateResponseDTO();
            cityCoordinateResponseDTO.setLatitude(centerLat);
            cityCoordinateResponseDTO.setLongitude(centerLng);
            cityCoordinateResponseDTO.setCityName(queryDTO.getCityName());

            // 7. 构建分页响应
            PagedAttractionResponse response = new PagedAttractionResponse(
                    attractions,
                    cityCoordinateResponseDTO,
                    queryDTO.getIndex(),
                    queryDTO.getCount(),
                    totalCount.intValue(),
                    (offset + queryDTO.getCount()) < totalCount,
                    totalCount > 1000 ? "1万+" : String.valueOf(totalCount)
            );

            log.info("查询附近景点完成，中心点: [{}, {}]，半径: {}km，找到 {} 个景点",
                    centerLat, centerLng, queryDTO.getRadius(), attractions.size());

            return response;

        } catch (Exception e) {
            log.error("查询附近景点失败", e);
            throw e;
        }
    }

    public List<Attraction> getUnfetchedDetailProducts() {
        QueryWrapper<Attraction> wrapper = new QueryWrapper<>();
        wrapper.eq("detail_fetched", false);
        return list(wrapper);
    }

    public List<Attraction> getUnfetchAttractionTourRecommend() {
        QueryWrapper<Attraction> wrapper = new QueryWrapper<>();
        wrapper.or(w -> w.isNull("visit_hours").or().eq("visit_hours", ""));
        return list(wrapper);
    }

    public void updateUnAttractionTourRecommend(Long poiId, TourRecommendationResponse recommendation) {
        LambdaUpdateWrapper<Attraction> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(Attraction::getPoiId, poiId)
                .set(Attraction::getVisitHours, recommendation.getRecommend_Hours());

        boolean updated = this.update(updateWrapper);
        if (!updated) {
            throw new RuntimeException("更新景点" + poiId + "失败");
        }
    }

    public boolean batchUpdateVisitHours(List<Attraction> attractions) {
        if (attractions.isEmpty()) {
            return true;
        }

        return this.updateBatchById(attractions, attractions.size());
    }

    public boolean deleteAttractionById(String poId) {
        Attraction byId = getOne(new QueryWrapper<Attraction>().eq("poi_id", poId));
        if (byId == null) {
            throw new RuntimeException("景点不存在");
        }

        boolean deleted = removeById(byId.getId());
        if (deleted) {
            log.info("成功删除景点: {}", poId);

            // 删除向量数据
            qdrantService.deleteAttractionVector(poId);

            log.info("成功删除景点{}的向量数据", poId);
            return true;
        } else {
            log.error("删除景点{}失败", poId);
            return false;
        }
    }

    @Override
    public AttractionResult getAttractionsWithResult(AttractionQueryDTO queryDTO) {
        try {
            log.info("查询景点列表，参数: {}", queryDTO);

            // 初始化统计信息
            AttractionResult result = new AttractionResult();
            result.setMessage("开始抓取数据");

            // 调用携程API获取实时数据
            CtripApiResponse apiResponse = ctripApiService.fetchAttractions(
                    queryDTO.getCityName(),
                    queryDTO.getIndex(),
                    queryDTO.getSortType(),
                    queryDTO.getCount(),
                    queryDTO.getFilterItems(),
                    queryDTO.getKeyword()
            );

            if (apiResponse == null || CollectionUtils.isEmpty(apiResponse.getAttractionList())) {
                PagedAttractionResponse emptyResponse = new PagedAttractionResponse(
                        new ArrayList<>(),
                        queryDTO.getIndex(),
                        queryDTO.getCount(),
                        0,
                        false,
                        "0"
                );
                result.setPagedResponse(emptyResponse);
                result.setMessage("未找到数据");
                return result;
            }

            List<CtripApiResponse.AttractionItem> attractionItems = apiResponse.getAttractionList();

            // 过滤逻辑保持不变
            List<String> excludeKeywords = Arrays.asList("演唱会", "巡回", "巡演", "音乐会", "话剧", "歌剧", "戏剧", "芭蕾", "舞蹈", "脱口秀", "决赛", "演艺秀","公开赛","观光巴士","专场",
                    "剧场", "喜剧", "剧院", "大剧院", "剧场", "影城", "影院", "电影院", "电影城", "电影院线", "影剧院", "演出", "展览", "体验展","舞剧","音乐剧","科普展","Live","Tour","演艺",
                    "图书馆", "书店", "图书室", "茶室", "棋牌", "网咖", "茶楼", "网吧", "台球", "酒吧", "KTV", "夜店", "夜总会", "迪厅", "迪吧", "夜场", "酒馆", "PUB", "LIVEHOUSE","沉浸式",
                    "SPA", "推拿", "游戏馆", "AR", "俱乐部", "会议中心", "会展中心", "会馆", "会所", "购物", "商场", "超市", "购物中心", "购物广场","大展","空间展","探索体验","见面会","舞台剧",
                    "魔术剧","作品","工作室","民宿","酒店","客栈","青年旅舍","旅馆","宾馆","住宿","旅社","驿站","农家乐","营地","露营地","公寓","写真","摄影","摄影基地","农场","果园","菜园","戏院",
                    "汉服","和服","旗袍","古装","租赁","出租","租借","试穿","体验馆","拍照","摄影棚","摄影馆","婚纱","婚庆","婚礼","婚宴","婚纱照","婚纱摄影","婚纱礼服","婚纱租赁","婚纱工作室","茶馆",
                    "婚纱影楼","婚纱基地","亲子乐园","冠军","赛场","球场","体育场","体育馆","运动场","运动馆","体育中心","体育公园","运动公园","高尔夫","滑雪场","滑雪馆","滑冰场","滑冰馆","赛事",
                    "影像展","下雪秀","流变展","汤泉","温泉","水疗","水城","影音","旅拍","家庭");

            List<CtripApiResponse.AttractionItem> filteredAttractions = attractionItems.stream()
                    .filter(item -> {
                        String poiName = item.getCard() != null ? item.getCard().getPoiName() : "";
                        return excludeKeywords.stream().noneMatch(poiName::contains);
                    })
                    .toList();

            // 设置总抓取数
            result.setTotalCount(filteredAttractions.size());

            // 并发处理景点数据（保存基本信息 + 抓取详情）- 带统计
            ProcessResult processResult = processConcurrentAttractionsWithStats(filteredAttractions);

            // 更新统计信息
            result.setNewCount(processResult.getNewCount());
            result.setUpdateCount(processResult.getUpdateCount());
            result.setMessage(String.format("抓取完成: 总数 %d, 新增 %d, 更新 %d",
                    result.getTotalCount(), result.getNewCount(), result.getUpdateCount()));

            // 保存筛选条件数据
            if (apiResponse.getFilters() != null) {
                saveFilterOptions(apiResponse.getFilters());
            }

            // 构建分页响应
            PagedAttractionResponse pagedResponse = new PagedAttractionResponse(
                    processResult.getAttractionList(),
                    queryDTO.getIndex(),
                    queryDTO.getCount(),
                    apiResponse.getTotalCount(),
                    apiResponse.getHasMore(),
                    apiResponse.getTotalCountDesc()
            );

            result.setPagedResponse(pagedResponse);

            log.info("抓取完成: 总数 {}, 新增 {}, 更新 {}",
                    result.getTotalCount(), result.getNewCount(), result.getUpdateCount());

            return result;

        } catch (Exception e) {
            log.error("查询景点列表失败，使用数据库数据", e);
            AttractionResult attractionResult = new AttractionResult();
            attractionResult.setPagedResponse(getAttractionsFromDatabase(queryDTO));
            return attractionResult;
        }
    }

    // 3. 创建处理结果类
    @Data
    public static class ProcessResult {
        private List<AttractionResponseDTO> attractionList;
        private Integer newCount = 0;
        private Integer updateCount = 0;
    }


}